package com.etonenet.base.test;

import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

public class BaseUserDetails implements UserDetails {
	private static final long serialVersionUID = 1L;
	protected String password;
	protected String username;
	protected Collection<? extends GrantedAuthority> authorities;
	protected boolean accountNonExpired;
	protected boolean accountNonLocked;
	protected boolean credentialsNonExpired;
	protected boolean enabled;

	public BaseUserDetails(String username, String password, boolean enabled,
			Collection<? extends GrantedAuthority> authorities) throws IllegalArgumentException {
		this(username, password, enabled, true, true, authorities);
	}

	public BaseUserDetails(String username, String password, boolean enabled, boolean accountNonExpired,
			boolean credentialsNonExpired, Collection<? extends GrantedAuthority> authorities)
			throws IllegalArgumentException {
		this(username, password, enabled, accountNonExpired, credentialsNonExpired, true, authorities);
	}

	public BaseUserDetails(String username, String password, boolean enabled, boolean accountNonExpired,
			boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities)
			throws IllegalArgumentException {
		if (((username == null) || "".equals(username)) || (password == null)) {
			throw new IllegalArgumentException("Cannot pass null or empty values to constructor");
		}

		this.username = username;
		this.password = password;
		this.enabled = enabled;
		this.accountNonExpired = accountNonExpired;
		this.credentialsNonExpired = credentialsNonExpired;
		this.accountNonLocked = accountNonLocked;
		setAuthorities(authorities);
	}

	public boolean equals(Object rhs) {
		if (!(rhs instanceof BaseUserDetails) || (rhs == null)) {
			return false;
		}

		BaseUserDetails user = (BaseUserDetails) rhs;

		// We rely on constructor to guarantee any User has non-null and >0
		// authorities
		if (user.getAuthorities().size() != this.getAuthorities().size()) {
			return false;
		}

		if (!CollectionUtils.containsAny(getAuthorities(), user.getAuthorities())) {
			return false;
		}

		if (!CollectionUtils.containsAny(user.getAuthorities(), getAuthorities())) {
			return false;
		}

		// We rely on constructor to guarantee non-null username and password
		return (this.getPassword().equals(user.getPassword()) && this.getUsername().equals(user.getUsername())
				&& (this.isAccountNonExpired() == user.isAccountNonExpired())
				&& (this.isAccountNonLocked() == user.isAccountNonLocked())
				&& (this.isCredentialsNonExpired() == user.isCredentialsNonExpired())
				&& (this.isEnabled() == user.isEnabled()));
	}

	public String getPassword() {
		return password;
	}

	public String getUsername() {
		return username;
	}

	public int hashCode() {
		int code = 9792;

		if (this.getAuthorities() != null) {
			for (GrantedAuthority grantedAuthority : authorities) {
				code = code * (grantedAuthority.hashCode() % 7);
			}
		}

		if (this.getPassword() != null) {
			code = code * (this.getPassword().hashCode() % 7);
		}

		if (this.getUsername() != null) {
			code = code * (this.getUsername().hashCode() % 7);
		}

		if (this.isAccountNonExpired()) {
			code = code * -2;
		}

		if (this.isAccountNonLocked()) {
			code = code * -3;
		}

		if (this.isCredentialsNonExpired()) {
			code = code * -5;
		}

		if (this.isEnabled()) {
			code = code * -7;
		}

		return code;
	}

	public boolean isAccountNonExpired() {
		return accountNonExpired;
	}

	public boolean isAccountNonLocked() {
		return this.accountNonLocked;
	}

	public boolean isCredentialsNonExpired() {
		return credentialsNonExpired;
	}

	public boolean isEnabled() {
		return enabled;
	}

	public void expireAccount() {
		this.accountNonExpired = false;
	}

	public void lockAccount() {
		this.accountNonLocked = false;
	}

	public void expireCredential() {
		this.credentialsNonExpired = false;
	}

	public void disable() {
		this.enabled = false;
	}

	protected void setAuthorities(Collection<? extends GrantedAuthority> authorities) {
		Assert.notNull(authorities, "Cannot pass a null GrantedAuthority array");

		// Ensure array iteration order is predictable (as per
		// UserDetails.getAuthorities() contract and SEC-xxx)
//		SortedSet<GrantedAuthority> sorter = new TreeSet<GrantedAuthority>();
//		for (GrantedAuthority grantedAuthority : authorities) {
//			Assert.notNull(grantedAuthority, "Collection<? extends GrantedAuthority> cannot contain any null elements");
//			sorter.add(grantedAuthority);
//		}

		this.authorities = authorities;
	}

	public String toString() {
		StringBuffer sb = new StringBuffer();
		sb.append(super.toString()).append(": ");
		sb.append("Username: ").append(this.username).append("; ");
		sb.append("Password: [PROTECTED]; ");
		sb.append("Enabled: ").append(this.enabled).append("; ");
		sb.append("AccountNonExpired: ").append(this.accountNonExpired).append("; ");
		sb.append("credentialsNonExpired: ").append(this.credentialsNonExpired).append("; ");
		sb.append("AccountNonLocked: ").append(this.accountNonLocked).append("; ");

		if (this.getAuthorities() != null) {
			sb.append("Granted Authorities: ");
			sb.append(StringUtils.collectionToCommaDelimitedString(getAuthorities()));

		} else {
			sb.append("Not granted any authorities");
		}

		return sb.toString();
	}

	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		return authorities;
	}
}
